Skip to main content

Connection Limiting

Connection limiting restricts the number of simultaneous (concurrent) connections a client can open to your NGINX server.

It is implemented using the limit_conn module and protects against:

  • Slowloris attacks
  • Connection-flood DDoS attacks
  • Resource exhaustion (worker connections, memory)
  • Abuse from bots and crawlers
  • Misbehaving clients holding connections open

Key difference from rate limiting:

limit_conn limits how many connections are open at the same time, not how many requests are sent per second.

How limit_conn Works (Internals)

NGINX maintains a shared memory counter:

  1. Each new connection increments a counter
  2. Counter is keyed by:
    • IP address
    • Server
    • Custom variable
  3. When the limit is exceeded:
    • Connection is rejected immediately
  4. Counter is decremented when the connection closes

This is a hard limit (no queuing, no delays).

Core Directives of limit_conn

limit_conn_zone (Define the Connection Zone)

limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
ComponentMeaning
$binary_remote_addrClient identifier (IP)
zone=conn_limit:10mShared memory zone
10mStores ~160,000 unique keys

Uses binary format → more memory-efficient

limit_conn (Apply the Limit)

limit_conn conn_limit 10;
PartMeaning
conn_limitZone name
10Max concurrent connections

limit_conn_status (Custom Status Code)

limit_conn_status 429;

Default is 503 Service Unavailable

Basic Example: Limit Connections Per IP

http {
limit_conn_zone $binary_remote_addr zone=per_ip:10m;

server {
listen 80;

location / {
limit_conn per_ip 10;
}
}
}
  • Each IP can open 10 concurrent connections
  • 11th connection is rejected

Protecting Against Slowloris Attacks

Slowloris holds many connections open with slow headers.

limit_conn_zone $binary_remote_addr zone=slowloris:10m;

server {
listen 443 ssl;

limit_conn slowloris 5;

client_header_timeout 10s;
client_body_timeout 10s;
}
  • Max 5 open connections per IP
  • Timeouts close slow connections

Limiting Connections Per Server (Global Cap)

limit_conn_zone $server_name zone=per_server:10m;

server {
listen 80;
limit_conn per_server 1000;
}
  • Backend has limited capacity
  • You want a hard cap per virtual host

API / WebSocket Connection Limiting

WebSockets keep connections open for a long time.

limit_conn_zone $binary_remote_addr zone=ws:10m;

location /ws/ {
limit_conn ws 2;
proxy_pass http://ws_backend;
}

Combining limit_conn with limit_req

limit_req_zone  $binary_remote_addr zone=req:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=conn:10m;

server {
listen 443 ssl;

location /api/ {
limit_req zone=req burst=20 nodelay;
limit_conn conn 10;
proxy_pass http://api_backend;
}
}
ThreatProtection
HTTP floodlimit_req
Slow clientslimit_conn
Resource exhaustionBoth

Limiting Connections Behind a Load Balancer

Fix Real Client IP

set_real_ip_from 10.0.0.0/8;
real_ip_header X-Forwarded-For;

Then:

limit_conn_zone $binary_remote_addr zone=realip:10m;
limit_conn realip 20;

📌 Without this:

  • All users appear as one IP
  • Limits become useless

Common Security Mistakes

MistakeRisk
Too high connection limitIneffective protection
Too low limitLegit users blocked
Not setting timeoutsSlowloris still possible
No real IP handlingAll users share same bucket
Only using limit_connHTTP floods still succeed

Testing Connection Limits

Open Multiple Connections

ab -n 1000 -c 50 http://example.com/

Expected: Some requests fail with 429 or 503

Check logs

error_log /var/log/nginx/error.log notice;

limit_conn vs limit_req

Featurelimit_connlimit_req
LimitsConcurrent connectionsRequests per second
Queueing❌ No✔ Yes
Best forSlowloris, WebSocketsAPIs, login endpoints
limit_conn_zone $binary_remote_addr zone=conn:10m;
limit_conn_status 429;

server {
limit_conn conn 10;
client_header_timeout 10s;
client_body_timeout 10s;
}